// ============================================================================
//  gWeapon.uc ::
// ============================================================================
class gWeapon extends TFWeapon
    Abstract
    HideDropDown
    CacheExempt
    Config;


// AIRating constants
// Configured AIRating must be >= 2.0 && <= 3.0
// Cumulative penalties must not exceed 1.0
const RATING_Skip       = -2.0; // Don't even think about it
const RATING_Avoid      = 0.1;  // Nearly useless
const RATING_Poor       = 0.5;  // Not very effective
const RATING_Base       = 2.0;  //



// - Weapon Sway --------------------------------------------------------------

var() globalconfig byte SwayMode;
var() bool              bSwayReset;

var() float             SwayMaxRotY;            // How far the gun can sway
var() float             SwayMaxSpeedY;          // How fast the gun can sway
var() float             SwayMaxCenterSpeedY;    // How fast the gun can center
var() float             SwayMaxDeltaSpeedY;     // View rotation speed unit
var() float             SwayDisplacementY;      // View offset sway multiplier

var() float             SwayMaxRotP;            // How far the gun can sway
var() float             SwayMaxSpeedP;          // How fast the gun can sway
var() float             SwayMaxCenterSpeedP;    // How fast the gun can center
var() float             SwayMaxDeltaSpeedP;     // View rotation speed unit
var() float             SwayDisplacementP;      // View offset sway multiplier

var   float             SwayLastTime;
var   rotator           SwayRot;
var   rotator           SwayLastRot;
var   rotator           SwayDefaultPivot;
var   float             SwayCenterDurationY;
var   float             SwayCenterTimeY;
var   float             SwayCenterDurationP;
var   float             SwayCenterTimeP;


// - Render -------------------------------------------------------------------

var() bool              bSpecialDrawWeapon;
var() bool              bForceViewUpdate;


var() float             SelectSoundVolume;

var() int               ItemSize;

var   xPawn             DeadHolder;

var() float             CostWeapon;
var() float             CostAmmo;
var() float             WarrantyPercent;
var() int               WarrantyMode;
var   int               WarrantyDamage;

var() Material          IconFlashMaterial;

var() float             BotPurchaseProbMod;

var() Texture          OverrideCrosshair;

var() array<int>        AmmoShopping;

var() float             HUDAmmoWidth;
var() float             HUDAmmoHeight;

var() float             IdleAnimTween;

var() float             BarFullSteady;

var   float             InaccuracyLevel[2];

// ============================================================================
//  Projectile
// ============================================================================

simulated function RegisterProjectile( gProjectile P );
simulated function UnRegisterProjectile( gProjectile P );

// ============================================================================
//  Animation
// ============================================================================

simulated event AnimEnd(int channel)
{
    local name anim;
    local float frame, rate;

    GetAnimParams(0, anim, frame, rate);

    if( ClientState == WS_ReadyToFire )
    {
        if( anim == FireMode[0].FireAnim && HasAnim(FireMode[0].FireEndAnim) ) // rocket hack
        {
            PlayAnim(FireMode[0].FireEndAnim, FireMode[0].FireEndAnimRate, 0.0);
        }
        else if( anim == FireMode[1].FireAnim && HasAnim(FireMode[1].FireEndAnim) )
        {
            PlayAnim(FireMode[1].FireEndAnim, FireMode[1].FireEndAnimRate, 0.0);
        }
        else if( (FireMode[0] == None || !FireMode[0].bIsFiring) && (FireMode[1] == None || !FireMode[1].bIsFiring) )
        {
            PlayIdle();
        }
    }
}

simulated function PlayIdle()
{
    LoopAnim(IdleAnim, IdleAnimRate, IdleAnimTween);
}

// ============================================================================
//  Firing
// ============================================================================

simulated function bool StartFire(int Mode)
{
    if( !ReadyToFire(Mode) )
        return False;

    return Super.StartFire(Mode);
}

// ============================================================================
//  Ammo
// ============================================================================

function GiveAmmo(int m, WeaponPickup WP, bool bJustSpawned)
{
    local bool bJustSpawnedAmmo;
    local int addAmount, InitialAmount;

    //gLog( "GiveAmmo" #m #WP #bJustSpawned );

    if( FireMode[m] != None && FireMode[m].AmmoClass != None )
    {
        Ammo[m] = Ammunition(Instigator.FindInventoryType(FireMode[m].AmmoClass));
        bJustSpawnedAmmo = False;

        if( bNoAmmoInstances )
        {
            if( (FireMode[m].AmmoClass == None) || ((m != 0) && (FireMode[m].AmmoClass == FireMode[0].AmmoClass)) )
                return;

            InitialAmount = FireMode[m].AmmoClass.Default.InitialAmount;
            if( WP != None )
            {
                InitialAmount = WP.AmmoAmount[m];
            }

            if( Ammo[m] != None )
            {
                addamount = InitialAmount + Ammo[m].AmmoAmount;
                Ammo[m].Destroy();
            }
            else
                addAmount = InitialAmount;

            AddAmmo(addAmount,m);
        }
        else
        {
            if( (Ammo[m] == None) && (FireMode[m].AmmoClass != None) )
            {
                Ammo[m] = Spawn(FireMode[m].AmmoClass, Instigator);
                Instigator.AddInventory(Ammo[m]);
                bJustSpawnedAmmo = True;
            }
            else if( (m == 0) || (FireMode[m].AmmoClass != FireMode[0].AmmoClass) )
                bJustSpawnedAmmo = ( bJustSpawned || ((WP != None) && !WP.bWeaponStay) );

            if((WP != None) && ((WP.AmmoAmount[0] > 0) || (WP.AmmoAmount[1] > 0)) )
            {
                addAmount = WP.AmmoAmount[m];
            }
            else if( bJustSpawnedAmmo )
            {
                addAmount = Ammo[m].InitialAmount;
            }

            Ammo[m].AddAmmo(addAmount);
            Ammo[m].GotoState('');
        }
    }
}

simulated function OutOfAmmo()
{
    //if( !bSwitchIfNoAmmo )
        return;

    if( Instigator == none || !Instigator.IsLocallyControlled() || HasAmmo() )
        return;

    DoAutoSwitch();
}

// ============================================================================
//  Weapon Sway
// ============================================================================

simulated function CalcWeaponDrag(rotator ViewRot)
{
    local float OldSwayMaxSpeedY, OldSwayMaxSpeedP;

    // If SpeedY is negative and SpeedP positive we get drag behaviour
    OldSwayMaxSpeedY = SwayMaxSpeedY;
    OldSwayMaxSpeedP = SwayMaxSpeedP;
    SwayMaxSpeedY = Abs(SwayMaxSpeedY) * -1.0;
    SwayMaxSpeedP = Abs(SwayMaxSpeedP);

    CalcWeaponSway(ViewRot);

    SwayMaxSpeedY = OldSwayMaxSpeedY;
    SwayMaxSpeedP = OldSwayMaxSpeedP;
}

simulated function CalcWeaponSway(rotator ViewRot)
{
    local float DT, DeltaRotY, SwayRotY, AlphaDeltaY, AlphaSwayY, AlphaCenterY, AlphaDelayY;
    local float SpeedDeltaY, SpeedSwayY, SpeedCenterY;
    local float DeltaRotP, SwayRotP, AlphaDeltaP, AlphaSwayP, AlphaCenterP, AlphaDelayP;
    local float SpeedDeltaP, SpeedSwayP, SpeedCenterP;
    local rotator DeltaRot, DispRot;

    if( bSwayReset )
    {
        SwayRot = rot(0,0,0);
        SwayLastTime = Level.TimeSeconds;
        bSwayReset = False;
        return;
    }

    // Time
    DT = Level.TimeSeconds - SwayLastTime;
    SwayLastTime = Level.TimeSeconds;

    // Rotation
    DeltaRot = ViewRot - SwayLastRot;
    SwayLastRot = ViewRot;


    // - BEGIN YAW -------------------------------------------------------------

    // Cache
    DeltaRotY = Normalize(DeltaRot).Yaw;
    SwayRotY = Normalize(SwayRot).Yaw;

    // View rotation speed in RU/s
    // How fast the view is being rotated, 1=high
    SpeedDeltaY = DeltaRotY / DT;
    AlphaDeltaY = FClamp( SpeedDeltaY / SwayMaxDeltaSpeedY, -1, 1 );

    // How far the gun is swayed, 1=far
    // Sway speed in RU/s
    AlphaSwayY = SwayRotY / SwayMaxRotY;
    SpeedSwayY = SwayMaxSpeedY * AlphaDeltaY;

    // How fast the gun should center
    AlphaCenterY = AlphaSwayY;

    // If the view is rotating
    if( Abs(DeltaRotY) > 0 )
    {
        // Center speed fade in
        SwayCenterDurationY = 0.25; //0.2 + 0.15 * FRand();
        SwayCenterTimeY = SwayCenterDurationY;

        // Center speed
        SpeedCenterY = SwayMaxCenterSpeedY * AlphaCenterY;
    }

    // If the view doesn't rotate
    else
    {
        // Center speed fade in
        AlphaDelayY = 1 - ( SwayCenterTimeY / SwayCenterDurationY );
        SwayCenterTimeY -= FMin( DT, SwayCenterTimeY );

        // Center speed
        SpeedCenterY = SwayMaxCenterSpeedY * AlphaCenterY * AlphaDelayY;
    }

    // Add rotation speed and center speed
    SwayRot.Yaw += (SpeedSwayY - SpeedCenterY) * DT;

    // - END YAW ---------------------------------------------------------------

    // - BEGIN PITCH -----------------------------------------------------------

    // Cache
    DeltaRotP = Normalize(DeltaRot).Pitch;
    SwayRotP = Normalize(SwayRot).Pitch;

    // View rotation speed in RU/s
    // How fast the view is being rotated, 1=high
    SpeedDeltaP = DeltaRotP / DT;
    AlphaDeltaP = FClamp( SpeedDeltaP / SwayMaxDeltaSpeedP, -1, 1 );

    // How far the gun is swayed, 1=far
    // Sway speed in RU/s
    AlphaSwayP = SwayRotP / SwayMaxRotP;
    SpeedSwayP = SwayMaxSpeedP * AlphaDeltaP;

    // How fast the gun should center
    AlphaCenterP = AlphaSwayP;

    // If the view is rotating
    if( Abs(DeltaRotP) > 0 )
    {
        // Center speed fade in
        SwayCenterDurationP = 0.25; //0.2 + 0.15 * FRand();
        SwayCenterTimeP = SwayCenterDurationP;

        // Center speed
        SpeedCenterP = SwayMaxCenterSpeedP * AlphaCenterP;
    }

    // If the view doesn't rotate
    else
    {
        // Center speed fade in
        AlphaDelayP = 1 - ( SwayCenterTimeP / SwayCenterDurationP );
        SwayCenterTimeP -= FMin( DT, SwayCenterTimeP );

        // Center speed
        SpeedCenterP = SwayMaxCenterSpeedP * AlphaCenterP * AlphaDelayP;
    }

    // Add rotation speed and center speed
    SwayRot.Pitch += (SpeedSwayP - SpeedCenterP) * DT;

    // - END PITCH -------------------------------------------------------------

    // Keep sway normalized
    SwayRot = Normalize(SwayRot);

    // Weapon Pivot
    PlayerViewPivot = SwayDefaultPivot + SwayRot;

    // Weapon Offset
    DispRot.Pitch = (float(SwayRot.Pitch) * SwayDisplacementP);
    DispRot.Yaw = (float(SwayRot.Yaw) * SwayDisplacementY);
    PlayerViewOffset = PlayerViewOffset >> DispRot;

//    gLog( "CV"
//        #byte(Weapon.bInitOldMesh)
//        #DT
//        #Weapon.PlayerViewOffset
//        #Weapon.PlayerViewPivot
//    );

//    if( DeltaRotP != 0 )
//    {
//        gLog(
//            SwayRot.Pitch
//        @   DeltaRot.Pitch
//        @   ViewRot.Pitch
//        @   AlphaDeltaP
//        @   AlphaSwayP
//        @   AlphaCenterP
//        @   SpeedDeltaP
//        @   DeltaRotP
//        );
//    }
}

// ============================================================================
//  Effects
// ============================================================================

simulated function IncrementFlashCount(int Mode)
{
    if( gWeaponAttachment(ThirdPersonActor) != None )
        gWeaponAttachment(ThirdPersonActor).FlashCountIncrement(Mode);
}

simulated function ZeroFlashCount(int Mode)
{
    if( gWeaponAttachment(ThirdPersonActor) != None )
        gWeaponAttachment(ThirdPersonActor).FlashCountZero(Mode);
}


simulated function SetOverlayMaterial( Material mat, float time, bool bOverride )
{
    //gLog( "SetOverlayMaterial" #mat #time #bOverride );

    // UDamage overlay cannot be overriden with another one
    if( mat != None && OverlayMaterial != None && mat != OverlayMaterial && OverlayMaterial == class'xPawn'.default.UDamageWeaponMaterial )
        return;

    // UDamage always overrides
    if( mat == class'xPawn'.default.UDamageWeaponMaterial )
        bOverride = True;

    Super.SetOverlayMaterial(mat,time,bOverride);
}

// ============================================================================
//  DefaultProperties
// ============================================================================

defaultproperties
{
     SwayMaxRotY=2048.000000
     SwayMaxSpeedY=13500.000000
     SwayMaxCenterSpeedY=12000.000000
     SwayMaxDeltaSpeedY=65535.000000
     SwayDisplacementY=0.670000
     SwayMaxRotP=1024.000000
     SwayMaxSpeedP=8192.000000
     SwayMaxCenterSpeedP=10000.000000
     SwayMaxDeltaSpeedP=65535.000000
     SwayDisplacementP=-2.000000
     SelectSoundVolume=0.700000
     ItemSize=1
     CostWeapon=750.000000
     CostAmmo=200.000000
     WarrantyPercent=0.300000
     WarrantyDamage=200
     IconFlashMaterial=FinalBlend'TFMedia.Gfx.fb_iconsheet0_pulse'
     BotPurchaseProbMod=1.000000
     AmmoShopping(0)=1
     HUDAmmoWidth=140.000000
     HUDAmmoHeight=45.000000
     IdleAnimTween=0.200000
     BarFullSteady=0.999999
     RestAnim=
     AimAnim=
     RunAnim=
     SelectAnimRate=1.000000
     PutDownAnimRate=1.000000
     AIRating=1.000000
     CurrentRating=1.000000
     Priority=255
     HudColor=(B=255)
     CenteredOffsetY=0.000000
     CenteredRoll=0
     CustomCrosshair=-1
     CustomCrossHairScale=0.800000
     CustomCrossHairTextureName="TFMedia.XHairs.crosshair_a5"
     InventoryGroup=255
     PlayerViewPivot=(Yaw=32768)
     BobDamping=0.500000
     IconMaterial=Texture'TFMedia.Gfx.IconSheet0'
     LightType=LT_Steady
     LightEffect=LE_NonIncidence
     AmbientGlow=0
}
